package org.example;

import java.io.*;
import java.net.Socket;

public class EasyFTP {
    public boolean isLog = false;      // 是否输出日志
    public String username;
    public String password;
    public String host = "127.0.0.1";  // 默认
    public int port = 21;              // 默认
    public Socket socket;

    public EasyFTP() {
    }

    public EasyFTP(String host, int port) {
        this.host = host;
        this.port = port;
    }

    /**
     * 连接到 FTP 服务器
     */
    private void connectFTP() {
        try {
            socket = new Socket(this.host, this.port);
            System.out.println("** 连接 FTP 服务器成功...");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 登录
     */
    public boolean login(String username, String password) {
        connectFTP();
        this.username = username;
        this.password = password;

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            writer.println("USER " + username);
            writer.println("PASS " + password);


            if (handleResponse(reader, "230")) {  // 230 Logged on
//                System.out.println("** 登录成功...");
                return true;
            }
            return false;
            // todo 这里需要判断是否登录成功
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 退出登录
     */
    public void logout() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            writer.println("QUIT");
            System.out.println("** 已退出登录 FTP...");
            handleResponse(reader, "221");    // 221 Goodbye

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件下载
     */
    public void download(String ftpFileName) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 让服务器进入被动模式
            writer.println("PASV");
            String pasvResponse = reader.readLine();

            // 解析被动模式返回的端口信息
            String[] parts = pasvResponse.split("[()]")[1].split(",");
            String ip = String.join(".", parts[0], parts[1], parts[2], parts[3]);
            int port = (Integer.parseInt(parts[4]) * 256) + Integer.parseInt(parts[5]);

            System.out.println("** 服务器IP：" + ip + " 端口：" + port);

            // 创建数据连接的 Socket
            Socket dataSocket = new Socket(ip, port);
            InputStream dataInputStream = dataSocket.getInputStream();
            writer.println("RETR " + ftpFileName);

            FileOutputStream fos = new FileOutputStream(ftpFileName);
            byte[] bytes = new byte[1024];
            int bytesRead;
            while ((bytesRead = dataInputStream.read(bytes)) != -1) {
                fos.write(bytes, 0, bytesRead);
                fos.flush();
            }

            fos.close();
            dataInputStream.close();
            dataSocket.close();
            if (handleResponse(reader, "226")) {
                // 226 Successfully transferred "/C语言.png"
                System.out.println("** 文件下载成功...");
            }
        } catch (Exception e) {
            // TODO: 2024/6/12 使用主动模式下载文件 暂时不封装
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件上传到工作目录
     */
    public void upload(String localFileName) {
        try {
            File file = new File(localFileName);  // 放在最前面，如果打开文件异常，就不会执行下面代码(直接抛出异常)
            FileInputStream fis = new FileInputStream(file);

            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 让服务器进入被动模式
            writer.println("PASV");
            String pasvResponse = reader.readLine();

            // 解析被动模式返回的端口信息
            String[] parts = pasvResponse.split("[()]")[1].split(",");
            String ip = String.join(".", parts[0], parts[1], parts[2], parts[3]);
            int port = (Integer.parseInt(parts[4]) * 256) + Integer.parseInt(parts[5]);

            System.out.println("** 服务器IP：" + ip + " 端口：" + port);
            Socket dataSocket = new Socket(ip, port);
            OutputStream dataOutputStream = dataSocket.getOutputStream();

            // 上传的文件名（路径）

            writer.println("STOR " + file.getName());
            byte[] bytes = new byte[1024];

            int bytesRead;
            while ((bytesRead = fis.read(bytes)) != -1) {
                dataOutputStream.write(bytes, 0, bytesRead);
            }
            fis.close();
            dataOutputStream.flush();
            dataOutputStream.close();
            dataSocket.close();

            handleResponse(reader, "226");  // 226 Successfully transferred "/evaluate.html"
            System.out.println("** 文件上传成功...");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 输出当前工作目录
     */
    public void printWorkingDirectory() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            writer.println("PWD");

            // 读取服务器响应
            String response = reader.readLine();

            // 提取工作目录路径
            if (response.startsWith("257")) {
                int firstQuote = response.indexOf('"');
                int lastQuote = response.lastIndexOf('"');
                if (firstQuote != -1 && lastQuote != -1 && firstQuote < lastQuote) {
                    String currentDirectory = response.substring(firstQuote + 1, lastQuote);
                    System.out.println("** 当前工作目录：" + currentDirectory);
                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 切换工作目录
     */
    public void changeWorkingDirectory(String newDirectory) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            writer.println("CWD " + newDirectory);
            handleResponse(reader, "250");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建文件夹
     */
    public void makeDirectory(String newDirectory) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
            writer.println("MKD " + newDirectory);

            if (handleResponse(reader, "257")) {
                System.out.println("** 成功创建目录：" + newDirectory);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 列出当前工作目录的文件列表
     */
    public void listFiles() {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

            // 让服务器进入被动模式
            writer.println("PASV");
            String pasvResponse = reader.readLine();

            // 解析被动模式返回的端口信息
            String[] parts = pasvResponse.split("[()]")[1].split(",");
            String ip = String.join(".", parts[0], parts[1], parts[2], parts[3]);
            int port = (Integer.parseInt(parts[4]) * 256) + Integer.parseInt(parts[5]);

            System.out.println("** 服务器IP：" + ip + " 端口：" + port);

            // 创建数据连接的 Socket
            Socket dataSocket = new Socket(ip, port);
            BufferedReader dataReader = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));

            writer.println("LIST");

            String line;
            while ((line = dataReader.readLine()) != null) {
                System.out.println(line);
            }

            handleResponse(reader, "226");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 处理从 FTP 返回的消息，
     *
     * @return 无异常 true，有异常 false
     */
    private boolean handleResponse(BufferedReader reader, String code) throws Exception {
        String msg;
        String responseCode;

        do {
            msg = reader.readLine();
            if (msg.length() >= 3) {
                responseCode = msg.substring(0, 3);
                if (isException(responseCode)) {
                    return false;
                }
            }

            if (isLog) {
                // 切换是否输出 FTP 服务器传过来的消息
                System.out.println(msg);
            }
        } while (!msg.startsWith(code));
        return true;
    }

    private boolean isException(String code) {
        switch (code) {
            case "530":
                System.out.println("** 登录失败...");
                return true;
            case "550":
                System.out.println("** 文件(夹)没找到 / 文件夹已存在...");
                return true;
            case "426":
                System.out.println("** Java 异常中断...");
                return true;
            default:
                return false;
        }
    }

    public static void main(String[] args) {
        String username = "fyx";
        String password = "123456";
        EasyFTP easyFTP = new EasyFTP();
        easyFTP.connectFTP();
        easyFTP.login(username, password);

//        easyFTP.download("C语言dd.png");
//        easyFTP.upload("evaluate33.html");
//
//        easyFTP.printWorkingDirectory();
//        easyFTP.changeWorkingDirectory("test666");
//        easyFTP.printWorkingDirectory();


        easyFTP.makeDirectory("666-2/30");
//        easyFTP.listFiles();
        easyFTP.logout();
    }
}
